#ifndef FACTORY_MODEL_H
#define FACTORY_MODEL_H

#include <map>
#include <functional>
#include <stdexcept>

class DevPtlModel;
class FactoryPtlModel {
public:
    template<typename T>
    struct register_ptl
    {
        register_ptl(const std::string& key)
        {
            FactoryPtlModel::get()._map.emplace(key, [] {return new T();});
        }

        template<typename... Args>
        register_ptl(const std::string& key, Args... args)
        {
            FactoryPtlModel::get()._map.emplace(key, [&]{return new T(args...);});
        }
    };

    static DevPtlModel* produce(const std::string& key)
    {
        auto it = _map.find(key);
        if(it == _map.end())
            throw std::invalid_argument("ptl invalid!");

        return it->second();  //_map[key]();
    }

private:
    FactoryPtlModel() {};
    FactoryPtlModel(const FactoryPtlModel&) = delete;
    FactoryPtlModel(FactoryPtlModel&&) = delete;

    static FactoryPtlModel& get()
    {
        static FactoryPtlModel ins;

        return ins;
    }

    static std::map<std::string, std::function<DevPtlModel *()>> _map;
};

std::map<std::string, std::function<DevPtlModel *()>> FactoryPtlModel::_map;

#define REGISTER_PTL_NAME(T) PTL_NAME_##T
#define REGISGER_PTL(T, key, ...) static FactoryPtlModel::register_ptl<T> REGISTER_PTL_NAME(T)(key, ##__VA_ARGS__);

#endif // FACTORY_MODEL_H
